/**
 * This class implements a simple data file reader/writer
 * @date 11/28
 */
#include "sysu_data.h"
#include "my_sys.h"
#include <string.h>

PSI_memory_key sysu_cmp_memory;

/**
 * init parameter
 * @author HuangDell
 */
Sysu_data::Sysu_data() {
    data_file=-1;
    number_records=-1;
    header_size=sizeof(bool)+sizeof(int)+sizeof(int);  // file header size
    record_header_size=sizeof(unsigned char)+sizeof(int);  // record header size
}

/**
 * @author HuangDell
 */
Sysu_data::~Sysu_data() {}

/**
 * create table by path
 * @param path table path
 */
int Sysu_data::create_table(char *path) {
    DBUG_ENTER("Sysu_data::create_table");
    open_table(path);
    number_records = 0;
    number_del_records = 0;
    crashed = false;  // no crashed
    write_header();
    DBUG_RETURN(0);
}

/**
 * open table if exists otherwise create it
 * @param path
 * @author HuangDell
 */
int Sysu_data::open_table(char *path) {
    DBUG_ENTER("Sysu_data::open_table");
    /*
     * open the file with read/write mode,create file if not found
     * and treat file as binary and use default flags
     */
    data_file = my_open(path, O_RDWR | O_CREAT | O_BINARY | O_SHARE, MYF(0));
    if (data_file)
        DBUG_RETURN(errno);
    read_header();
    DBUG_RETURN(0);
}

/**
 *写入一条记录
 * @param：
 *      buf指针: 指向数据本身的指针
 *      length: 数据的长度（byte）
 * @return：
 *      写入了多少个字节
 * @author:Chenwy
 */
long long Sysu_data::write_row(unsigned char *buf, int length)
{
    long long pos;
    int i;
    int len;
    unsigned char deleted = 0;

    DBUG_ENTER("Sysu_data::write_row");

    // 移动文件描述符到文件结尾，将新的记录添加到文件末尾
    pos = my_seek(data_file, 0, MY_SEEK_END, MYF(0));

    // 先写入一个byte：0表示后面的记录可用，没有被删除；1表示后面的记录不可用，被删除
    i = my_write(data_file, &deleted, sizeof(unsigned char), MYF(0));

    // len = length
    memcpy(&len, &length, sizeof(int));

    // 写入记录的长度
    i = my_write(data_file, (unsigned char*)&len, sizeof(int), MYF(0));

    // 往文件中写入数据
    i = my_write(data_file, buf, length, MYF(0));

    if (i == -1)
        pos = i; // 插入失败
    else
        ++number_records; // 成功插入

    DBUG_RETURN(pos);
}


/**
 * 更新一条记录
 * @param：
 *      old_rec：保存老数据值的指针
 *      new_rec：指向是新数据的指针
 * @return：
 * @author:Chenwy
 */
long long Sysu_data::update_row(unsigned char*old_rec, unsigned char*new_rec, int length, long long position)
{
    long long pos;
    long long cur_pos;
    unsigned char *cmp_rec;
    int len;
    unsigned char deleted = 0;
    int i = -1;
    DBUG_ENTER("Sysu_data::update_row");

    if (position == 0)
        position = header_size; // 移动到所有记录的开头

    pos = position;

    // position=-1：并不知道要更新的记录在哪里
    if (position == -1)
    {
        // 申请一块新的内存
        cmp_rec = (unsigned char*)my_malloc(sysu_cmp_memory, length, MYF(MY_ZEROFILL | MY_WME));
        pos = 0;
        // 将文件指针移动到文件开头
        cur_pos = my_seek(data_file, header_size, MY_SEEK_SET, MYF(0));
        // 开始遍历所有记录
        while ((cur_pos != -1) && (pos != -1))
        {
            pos = read_row(cmp_rec, length, cur_pos);
            // memcmp = 0：找到对应的记录
            if (memcmp(old_rec, cmp_rec, length) == 0)
            {
                pos = cur_pos;
                cur_pos = -1;
            }
            else if (pos != -1) {
                cur_pos = cur_pos + length + record_header_size;
            }
        }
        my_free(cmp_rec);
    }
    // 找到记录
    if (pos != -1)
    {
        // 文件描述符移动到记录所在位置
        my_seek(data_file, pos, MY_SEEK_SET, MYF(0));
        // 表示这个记录可用
        i = my_write(data_file, &deleted, sizeof(unsigned char), MYF(0));
        // len = length
        memcpy(&len, &length, sizeof(int));
        // 写入记录的长度
        i = my_write(data_file, (unsigned char*)&len, sizeof(int), MYF(0));
        // 记录记录所在位置
        pos = i;
        i = my_write(data_file, new_rec, length, MYF(0));
    }
    DBUG_RETURN(0);
}

/**
 * 读取一条记录
 * @param：
 *      buf：把记录放在buf指向的地址
 *      length：记录的长度
 *      position: 记录在文件中的位置
 *
 * @return
 * @author:Chenwy
*/
int Sysu_data::read_row(unsigned char*buf, int length, long long position)
{
    int i;
    int rec_len = 0;
    long long pos;
    unsigned char deleted = 2;
    DBUG_ENTER("Sysu_data::read_row");

    if (position <= 0)
        position = header_size;

    // move data_file to position
    pos = my_seek(data_file, position, MY_SEEK_SET, MYF(0));

    if (pos != -1)
    {
        // get the deleted information to check whether it is valid
        i = my_read(data_file, &deleted, sizeof(unsigned char), MYF(0));

        // valid
        if (deleted == 0)
        {
            // get the length of the record
            i = my_read(data_file, (unsigned char*)&rec_len, sizeof(int), MYF(0));
            // get the data of the record
            i = my_read(data_file, buf, (length < rec_len) ? length:rec_len, MYF(0));
        }else if (i == 0) // end of the file
            DBUG_RETURN(-1);
        else // recursion to search, also have problem
            DBUG_RETURN(read_row(buf, length, cur_position() + length + (record_header_size - sizeof(uchar))));
    }else
        DBUG_RETURN(-1);

    i = i + 1;
    DBUG_RETURN(0);

}

/**
 * 删除一条记录
 * @param:
 *      old_rec:要删除的记录的值
 *      length:记录的长度
 *      position:记录在文件中的位置
 * @return
 * @author:Chenwy
 * */
int Sysu_data::delete_row(unsigned char*old_rec, int length, long long position)
{
    long long pos;
    long long cur_pos;
    unsigned char *cmp_rec;
    unsigned char deleted = 1;
    int i = -1;
    DBUG_ENTER("Sysu_data::delete_row");

    if (position == 0)
        position = header_size; // 移动到所有记录的开头

    pos = position;

    // position=-1：并不知道要更新的记录在哪里
    if (position == -1)
    {
        // 申请一块新的内存
        cmp_rec = (unsigned char*)my_malloc(sysu_cmp_memory, length, MYF(MY_ZEROFILL | MY_WME));
        pos = 0;
        // 将文件指针移动到文件开头
        cur_pos = my_seek(data_file, header_size, MY_SEEK_SET, MYF(0));
        // 开始遍历所有记录
        while ((cur_pos != -1) && (pos != -1))
        {
            pos = read_row(cmp_rec, length, cur_pos);
            // memcmp = 0：找到对应的记录
            if (memcmp(old_rec, cmp_rec, length) == 0)
            {
                --number_records;
                ++number_del_records;
                pos = cur_pos;
                cur_pos = -1;
            }
            else if (pos != -1) {
                /**
                 * move to next record
                 * 注意：这里其实有一点问题 后期记得修改
                 */
                cur_pos = cur_pos + length + record_header_size;
            }
        }
        // free
        my_free(cmp_rec);
    }
    // 找到记录
    if (pos != -1)
    {
        // 文件描述符移动到记录所在位置
        pos = my_seek(data_file, pos, MY_SEEK_SET, MYF(0));
        // unvalid
        i = my_write(data_file, &deleted, sizeof(unsigned char), MYF(0));
        i = (i > 1) ? 0:i;
    }
    DBUG_RETURN(i);
}

/**
 * init parameter
 * read header from file
 * @author Hlx
 */
int Sysu_data::read_header() {
    int i = 0;
    int len;
    DBUG_ENTER("Sysu_data::read_header");
    if (number_records == -1) {
        my_seek(data_file, 0L, MY_SEEK_SET, MYF(0));
        i = my_read(data_file, (unsigned char * ) & crashed, sizeof(bool), MYF(0));
        i = my_read(data_file, (unsigned char * ) & len, sizeof(int), MYF(0));
        memcpy(&number_records, &len, sizeof(int));
        i = my_read(data_file, (unsigned char * ) & len, sizeof(int), MYF(0));
        memcpy(&number_del_records, &len, sizeof(int));
    } else {
        my_seek(data_file, header_size, MY_SEEK_SET, MYF(0));
    }
    i = i + 1;
    DBUG_RETURN(0);
}

/**
* init parameter
* write header to file
 * @author Hlx
*/
int Sysu_data::write_header() {
    int i = 0;
    DBUG_ENTER("Sysu_data::write_header");
    if (number_records != -1) {
        my_seek(data_file, 0L, MY_SEEK_SET, MYF(0));
        i = my_write(data_file, (unsigned char * ) & crashed, sizeof(bool), MYF(0));
        i = my_write(data_file, (unsigned char * ) & number_records, sizeof(int), MYF(0));
        i = my_write(data_file, (unsigned char * ) & number_del_records, sizeof(int), MYF(0));
    }
    i = i + 1;
    DBUG_RETURN(0);
}

/**
 * init parameter
 * truncate the data file
 * @author Hlx
 */
int Sysu_data::trunc_table() {
    DBUG_ENTER("Sysu_data::trunc_table");
    if (data_file != -1) {
        my_chsize(data_file, 0, 0, MYF(MY_WME));
        write_header();
    }
    DBUG_RETURN(0);
}


/**
 * init parameter
 * determine the row size of the data file
 * @author HLX
 */
int Sysu_data::row_size(int length) {
    DBUG_ENTER("Sysu_data::row_size");
    DBUG_RETURN(length + record_header_size);
}

/**
 * close file
 * @author JiangZhaohui
 */
int Sysu_data::close_table()
{
    DBUG_ENTER("Sysu_data::close_table");
    if (data_file != 1)
    {
        my_close(data_file, MYF(0));
        data_file = -1;
    }
    DBUG_RETURN(0);
}

/**
 * return number of records
 * @author JiangZhaohui
 */
int Sysu_data::records()
{
    DBUG_ENTER("Sysu_data::num_records");
    DBUG_RETURN(number_records);
}

/**
 * return number of deleted records
 * @author JiangZhaohui
 */
int Sysu_data::del_records()
{
    DBUG_ENTER("Sysu_data::num_del_records");
    DBUG_RETURN(number_del_records);
}

/**
 * get position of the data file
 * @author JiangZhaohui
 */
long long Sysu_data::cur_position()
{
    long long pos;

    DBUG_ENTER("Sysu_data::cur_position");
    pos = my_seek(data_file, 0L, MY_SEEK_CUR, MYF(0));
    if(pos==0)
        DBUG_RETURN(header_size);
    DBUG_RETURN(pos);
}
